import random
import string
import math
import re

from decimal import Decimal
from django.db import models
from django.core.validators import MinValueValidator, MaxValueValidator
from django.core.exceptions import ValidationError

from rest_framework import serializers


# Models manager for Quadratic calculations
class QuadraticManager(models.Manager):
    def nearby(self, latitude, longitude, max_distance=724204):
        """
        Возвращает места, находящиеся в пределах max_distance от заданной точки.
        450 миль ~ 724204 метров.
        """

        earth_radius = Decimal(6371210)
        max_distance = Decimal(max_distance)

        def computeDelta(degrees):
            return Decimal(math.pi) / Decimal('180') * Decimal(earth_radius) * Decimal(math.cos(degrees * Decimal(math.pi) / 180))

        deltaLat = computeDelta(latitude)  # Получаем дельту по широте
        deltaLon = computeDelta(longitude)  # Получаем дельту по долготе

        aroundLat = max_distance / deltaLat  # Вычисляем диапазон координат по широте
        aroundLon = max_distance / deltaLon  # Вычисляем диапазон координат по долготе

        queryset = super().get_queryset().filter(
            latitude__gte=Decimal(latitude - aroundLat),
            latitude__lte=Decimal(latitude + aroundLat),
            longitude__gte=Decimal(longitude - aroundLon),
            longitude__lte=Decimal(longitude + aroundLon),
        )
        return queryset


# Model definition for Location
class Location(models.Model):
    city = models.CharField(max_length=50)
    state = models.CharField(max_length=50)
    zipcode = models.IntegerField(validators=[MinValueValidator(1), MaxValueValidator(99999)], db_index=True, unique=True)
    latitude = models.DecimalField(max_digits=9, decimal_places=6)
    longitude = models.DecimalField(max_digits=9, decimal_places=6, db_index=True)

    objects = models.Manager()  # Creates a default manager
    quadratic = QuadraticManager()  # Creates a nearby manager

    def __str__(self):
        return self.city + ", " + self.state + " " + str(self.zipcode)


    class Meta:
        ordering = ['city', 'state', 'zipcode']
        indexes = [models.Index(fields=['latitude', 'longitude'])]  # индексация по zip, широте и долготе


# Model definition for Car
class Car(models.Model):
    unique_car_number = models.CharField(max_length=5,
                                         unique=True,
                                         blank=True,
                                         null=True,
                                         verbose_name="Уникальный номер. 4 цифры и 1 заглавная буква")
    current_location = models.ForeignKey(Location,
                                         on_delete=models.SET_NULL,
                                         blank=True,
                                         null=True)
    payload_type = models.IntegerField(validators=[MinValueValidator(1), MaxValueValidator(1000)])

    def __str__(self):
        return self.unique_car_number

    def generate_unique_number(self):
        """Генерирует уникальный номер."""
        while True:
            number = f"{random.randint(1000, 9999)}{random.choice(string.ascii_uppercase)}"
            if not Car.objects.filter(unique_car_number=number).exists():
                return number

    def generate_random_location(self):
        """Генерирует случайную местность."""
        location_ids = Location.objects.values_list('id', flat=True)
        if location_ids:
            random_location_id = random.choice(location_ids)
            random_location = Location.objects.get(id=random_location_id)
            return random_location

    def save(self, *args, **kwargs):
        pattern = r'^[1-9][0-9]{3}[A-Z]$'
        if not self.unique_car_number:
            self.unique_car_number = self.generate_unique_number()

        if not re.match(pattern, self.unique_car_number):
            raise ValidationError('Неправильный формат уникального номера автомобиля.')

        if not self.current_location:
            self.current_location = self.generate_random_location()

        super().save(*args, **kwargs)


# Model definition for Cargo
class Cargo(models.Model):
    pick_up_location = models.ForeignKey(Location, related_name='pick_up',
                                         verbose_name='Место отправления', on_delete=models.SET_NULL, null=True)
    deliver_location = models.ForeignKey(Location, related_name='deliver',
                                         verbose_name='Место доставки', on_delete=models.SET_NULL, null=True)
    cargo_weight = models.IntegerField(verbose_name='Вес', validators=[MinValueValidator(1), MaxValueValidator(1000)])
    description = models.TextField(verbose_name='Описание')

    def __str__(self):
        return self.description




